﻿using System;

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Practices.Mobile.ConnectionMonitor;
using Microsoft.Practices.Mobile.DisconnectedAgent;
using Microsoft.Practices.Mobile.DataAccess;
using Microsoft.Practices.Mobile.EndpointCatalog;
using Microsoft.Practices.Mobile.Configuration;
using System.Data.SqlServerCe;
using DSAGettingStarted.HelloWorldServiceProxy;

namespace DSAGettingStarted
{
    public partial class MainForm : Form
    {
        private ConnectionMonitor _connectionMonitor;
        private RequestManager _requestManager;
        private Database _database;
        private IEndpointCatalog _endpoints;
        private IConnectionMonitor _connectionMonitorAdapter; 

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            // Initialize disconnected service agent
            _connectionMonitor = ConnectionMonitorFactory.CreateFromConfiguration();
            _connectionMonitor.ConnectionStatusChanged += new EventHandler(_connectionMonitor_ConnectionStatusChanged);

            // Init database object
            string filename = System.IO.Path.Combine(DirectoryUtils.BaseDirectory, "AppDatabase.sdf");
            string connectionString = String.Format(@"Data Source=""{0}""", filename);
            if (!System.IO.File.Exists(filename))
            {
                SqlCeEngine eng = new SqlCeEngine(connectionString);
                eng.CreateDatabase();
            }
            _database = new SqlDatabase(connectionString);

            // ...and an IEndpointsCatalog
            IEndpointCatalogFactory factory = new EndpointCatalogFactory("Endpoints");
            _endpoints = factory.CreateCatalog();

            // Finally init the Agent, using the database, endpoint catalog and connection monitor
            _connectionMonitorAdapter = new ConnectionMonitorAdapter(_connectionMonitor);
            _requestManager = RequestManager.Instance;
            _requestManager.Initialize(_endpoints, _connectionMonitorAdapter, _database);

            // Hook the RequestDispatched event of the RequestManager, so we are notified of successful
            // despatch, but also of requests that have exceeded their retry count and have gone to the 
            // dead letter queue
            _requestManager.RequestDispatched += 
                new EventHandler<RequestDispatchedEventArgs>(_requestManager_RequestDispatched);

            _requestManager.StartAutomaticDispatch();

            DisplayStatus();
        }

        /// <summary>
        /// Event handler for ConnectionStatusChanged event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _connectionMonitor_ConnectionStatusChanged(object sender, EventArgs e)
        {
            DisplayStatus();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _requestManager_RequestDispatched(object sender, RequestDispatchedEventArgs e)
        {
            DisplayStatus();

            if (e.Result == DispatchResultStatus.Failed)
            {
                MessageBox.Show("Request failed and sent to Dead Letter Queue");
                // Action is app dependant. You may wish to requeue messages from the Dead Letter Queue
                // but be careful of introducing race conditions where a request is continually resubmitted
                // and continually fails.
            }
        }

        private delegate void DisplayStatusDelegate();

        private void DisplayStatus()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new DisplayStatusDelegate(DisplayStatus));
            }
            else
            {
                // Executing on the GUI thread, so set the Label control
                if (_connectionMonitor.IsConnected)
                {
                    foreach (Connection conn in _connectionMonitor.ActiveConnections)
                    {
                        lblConnectionStatus.Text = "Connected to " + conn.ConnectionTypeName + ", Price: " + conn.Price.ToString() + @"\r\n";
                    }
                }
                else
                {
                    lblConnectionStatus.Text = "Not connected to a network";
                }

                // List requests in the request queue
                dgRequestBindingSource.DataSource = _requestManager.RequestQueue.GetRequests();
                dgRequests.DataSource = dgRequestBindingSource;
                // and in the Dead letter queue
                dgDeadLetterBindingSource.DataSource = _requestManager.DeadLetterQueue.GetRequests();
                dgDeadLetter.DataSource = dgDeadLetterBindingSource;
            }        
        }

        private void Send10StampsMenuItem_Click(object sender, EventArgs e)
        {
            // Queue the order to the Disconnected Service Agent;
            QueueDSARequest("Message queued with 10 stamps at " + DateTime.Now.ToShortTimeString(), 10);
        }

        private void Send1StampMenuItem_Click(object sender, EventArgs e)
        {
            // Queue the order to the Disconnected Service Agent;
            QueueDSARequest("Message queued with 1 stamp at " + DateTime.Now.ToShortTimeString(), 1);
        }

        private void QueueDSARequest(string message, int numberStamps )
        {
            // Create our own OffLineBehaviour
            OfflineBehavior behavior = new OfflineBehavior();
            behavior.MaxRetries = 3;
            behavior.MessageId = Guid.NewGuid();  // IMPORTANT for Idempotency 
            behavior.Stamps = (uint)numberStamps;
            behavior.ReturnCallback = new CommandCallback(typeof(DSACallbacks), "OnSendSuccess");
            behavior.ExceptionCallback = new CommandCallback(typeof(DSACallbacks), "OnSendException");

            // Set up the request
            Request request = new Request();
            request.MethodName = "HelloWorld";
            request.Behavior = behavior;
            request.CallParameters = new Object[] { message };
            // Set the Web Service proxy type and endpoint name
            request.OnlineProxyType = typeof(HelloWorldService);
            request.Endpoint = "HelloWorldService";

            // Enqueue the request
            RequestManager.Instance.RequestQueue.Enqueue(request);

            DisplayStatus();
        }
    }

    public class DSACallbacks
    {
        public void OnSendSuccess(Request req , object[] qParams , string returnValue)
        {
            // Do something  with response from Web service
            MessageBox.Show("Callback from Disconnected Service Agent: " + returnValue);
        }

        public OnExceptionAction OnSendException(Request req , Exception ex )
        {
            // Exception in sending Web request
            MessageBox.Show("Exception Callback from Disconnected Service Agent: " + ex.Message);
            // Send back OnExceptionAction.Retry to retry the request immediately, max number of retries
            // is specified in the OfflineBehavior.MaxRetries property when submitting the request.
            // Send back OnExceptionAction.Dismiss to tell the RequestManager to give up on this one and to 
            // send it to the dead letter queue
            return OnExceptionAction.Retry;
        }
    }
}